package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"time"

	logger "github.com/alecthomas/log4go"
)

const configJson = `{
	"国企改分级" : {"母基" : "SH502003", "A基" : "SH502004", "B基" : "SH502005",},
	"军工分级" : {"母基" : "SH502006", "A基" : "SH502007", "B基" : "SH502008",},
	"50分级" : {"母基" : "SH502048", "A基" : "SH502049", "B基" : "SH5020SH50"}
}`

type FiveLevel [5]*PriceVol

type PriceVol struct {
	Price  float64
	Volume float64
}

type ObjInfo struct {
	Obj      string
	Time     int64
	NewPrice float64
	BuyFive  FiveLevel
	SellFive FiveLevel
}

//五档交易总量
func (this *ObjInfo) GetFiveTotalVol(buy bool) float64 {
	var totalVol float64
	for i := 0; i < 5; i++ {
		if buy {
			totalVol += this.BuyFive[i].Volume
		} else {
			totalVol += this.SellFive[i].Volume
		}
	}
	return totalVol
}

//获取模拟交易价
func (this *ObjInfo) GetTradePrice(buy bool, tradeVol float64) float64 {
	var tradeTotal float64
	var pv *PriceVol
	for i := 0; i < 5; i++ {
		if buy {
			pv = this.BuyFive[i]
		} else {
			pv = this.SellFive[i]
		}
		tradeTotal += pv.Volume
		if tradeTotal >= tradeVol {
			return pv.Price
		}
	}
	return pv.Price
}

//SH502006(M)->SH502006(M), SH502007(A)->SH502006(M), SH502008(B)->SH502006(M)
var fndMap = map[string]string{
	"SH502003": "SH502003", "SH502004": "SH502003", "SH502005": "SH502003", //军工分级
	"SH502006": "SH502006", "SH502007": "SH502006", "SH502008": "SH502006", //国企改分级
	"SH502048": "SH502048", "SH502049": "SH502048", "SH502050": "SH502050"} //上证50分级

//SH502003:{SH502003:ObjInfo, SH502004:ObjInfo, SH502005:ObjInfo}
var ObjInfoMap map[string]map[string]*ObjInfo //Ｍ与<obj,objinfo>映射
var newRatio float64                          //最新价折溢价率最低限
var tradeRatio float64                        //成交价折溢价率最低限
var tradeVol float64                          //交易手数
var opentime int64                            // AM 开盘时间
var AMclosetime int64                         // AM　收盘时间
var closetime int64                           // PM 收盘时间
var mu float64

func init() {
	flag.Float64Var(&newRatio, "nr", 0.7, "最新价折溢价最小达标值 default=0.7")
	flag.Float64Var(&tradeRatio, "tr", 0.5, "成交价折溢价最小达标值 default=0.5")
	flag.Float64Var(&tradeVol, "n", 500, "赠基操作买卖份额 default=50000")
	flag.Float64Var(&mu, "m", 2, "冗余乘数 default=2")
	initObjInfoMap()
}

func initObjInfoMap() {
	year, month, day := time.Now().Date()
	open_time := time.Date(year, month, day, 9, 30, 0, 0, time.Local).Unix()
	if open_time != opentime {
		opentime = open_time
		closetime = time.Date(year, month, day, 15, 0, 0, 0, time.Local).Unix()
		AMclosetime = time.Date(year, month, day, 11, 30, 0, 0, time.Local).Unix()
		ObjInfoMap = make(map[string]map[string]*ObjInfo, len(fndMap))
		for fnd, fnd_M := range fndMap {
			value, ok := ObjInfoMap[fnd_M]
			if !ok {
				value = make(map[string]*ObjInfo, 3)
				ObjInfoMap[fnd_M] = value
			}
			value[fnd] = nil
		}
		logger.Info("initObjInfoMap, opentime:%s", time.Unix(opentime, 0).Format("2006-01-02 15:04:05"))
	}
}

const objInfoUrl = "/json/objinfo/getinfo?response_times=-1&compr_type=nor&where=obj=%s&field=time,new,buyprice1,buyprice2,buyprice3,buyprice4,buyprice5,buyvol1,buyvol2,buyvol3,buyvol4,buyvol5,sellprice1,sellprice2,sellprice3,sellprice4,sellprice5,sellvol1,sellvol2,sellvol3,sellvol4,sellvol5"

func main() {
	flag.Parse()
	runtime.GOMAXPROCS(runtime.NumCPU())
	logger.LoadConfiguration("log.xml")
	defer func() {
		logger.Info("autotrade service stopped!")
		logger.Close()
	}()
	logger.Info("main-->quote_host: %s", quote_host)
	logger.Info("main-->trade_host: %s", trade_host)
	logger.Info("main-->new_ratio: %.1f", newRatio)
	logger.Info("main-->trade_ratio: %.1f", tradeRatio)
	logger.Info("main-->AM open: %s", time.Unix(opentime, 0).Format("2006-01-02 15:04:05"))
	logger.Info("main-->AM close: %s", time.Unix(AMclosetime, 0).Format("2006-01-02 15:04:05"))
	logger.Info("main-->PM close: %s", time.Unix(closetime, 0).Format("2006-01-02 15:04:05"))
	logger.Info("main-->trade_vol: %.1f", tradeVol)
	logger.Info("main-->mu: %.1f", mu)
	quoteClient := NewQuoteClient()
	if err := quoteClient.start(); err != nil {
		return
	}
	tradeClient := NewTradeClient()
	if err := tradeClient.start(); err != nil {
		return
	}
	var waitExit sync.WaitGroup
	waitExit.Add(1)
	go func() {
		defer waitExit.Done()
		//发送请求
		for fnd := range fndMap {
			reqUrl := fmt.Sprintf(objInfoUrl, fnd)
			quoteClient.sendreq(reqUrl)
		}
		for {
			infoRes, err := quoteClient.reciveres()
			if err != nil {
				return
			}
			if infoRes == nil { //清盘
				logger.Info("receive clear_data notify time: %s", time.Now().Format("2006-01-02 15:04:05"))
				initObjInfoMap()
				continue
			}
			if fnd_M, ok := fndMap[infoRes.Obj]; ok {
				fndInfoMap := ObjInfoMap[fnd_M]
				fndInfoMap[infoRes.Obj] = infoRes
				var M_Info, A_Info, B_Info *ObjInfo
				for fnd, fndInfo := range fndInfoMap {
					if fnd == fnd_M {
						M_Info = fndInfo
					} else if B_Info == nil {
						B_Info = fndInfo
					} else {
						A_Info = fndInfo
					}
				}
				if M_Info == nil || A_Info == nil ||
					B_Info == nil || infoRes.Time <= opentime {
					continue
				}
				MergeNewPrice := (A_Info.NewPrice + B_Info.NewPrice) / 2
				PDiff := M_Info.NewPrice - MergeNewPrice
				n_ratio := PDiff / MergeNewPrice * 100
				if n_ratio >= newRatio || n_ratio <= -newRatio {
					//大于等于设定ratio为溢价
					overFlow := n_ratio >= newRatio
					//1.A,B,M存在有可交易数量(使用2倍下单量)
					if A_Info.GetFiveTotalVol(!overFlow) < tradeVol/2*mu ||
						B_Info.GetFiveTotalVol(!overFlow) < tradeVol/2*mu ||
						A_Info.GetFiveTotalVol(overFlow) < mu*tradeVol {
						continue
					}
					//2.计算A,B,M最终成交价(使用2倍下单量)
					A_TradePrice := A_Info.GetTradePrice(!overFlow, tradeVol/2*mu)
					B_TradePrice := B_Info.GetTradePrice(!overFlow, tradeVol/2*mu)
					M_TradePrice := M_Info.GetTradePrice(overFlow, mu*tradeVol)
					//3.计算成交价溢价率
					MergeNewPrice = (A_TradePrice + B_TradePrice) / 2
					PDiff = M_TradePrice - MergeNewPrice
					t_ratio := PDiff / MergeNewPrice * 100
					logger.Debug("overflow_ratio(trade):%.3f(%.3f); %s|%s|%.3f;  %s|%s|%.3f; %s|%s|%.3f",
						n_ratio, t_ratio,
						M_Info.Obj, time.Unix(M_Info.Time, 0).Format("2006-01-02 15:04:05"), M_Info.NewPrice,
						A_Info.Obj, time.Unix(A_Info.Time, 0).Format("2006-01-02 15:04:05"), A_Info.NewPrice,
						B_Info.Obj, time.Unix(B_Info.Time, 0).Format("2006-01-02 15:04:05"), B_Info.NewPrice)
					if ((overFlow && t_ratio >= tradeRatio) ||
						(!overFlow && t_ratio <= -tradeRatio)) &&
						tradeClient.IsCanTrade() {
						logger.Info("-->>-----------------------------------------------------")
						logger.Info("-->>overflow_ratio(trade):%.3f(%.3f); %s|%s|%.3f(%.3f);  %s|%s|%.3f(%.3f); %s|%s|%.3f(%.3f);",
							n_ratio, t_ratio,
							M_Info.Obj, time.Unix(M_Info.Time, 0).Format("2006-01-02 15:04:05"), M_Info.NewPrice, M_TradePrice,
							A_Info.Obj, time.Unix(A_Info.Time, 0).Format("2006-01-02 15:04:05"), A_Info.NewPrice, A_TradePrice,
							B_Info.Obj, time.Unix(B_Info.Time, 0).Format("2006-01-02 15:04:05"), B_Info.NewPrice, B_TradePrice)
						if (infoRes.Time < closetime && closetime-infoRes.Time < 20) ||
							(infoRes.Time < AMclosetime && AMclosetime-infoRes.Time < 20) {
							logger.Info("From the closing time of less than 20 seconds, to skip this porcess!")
						} else {
							resStr, _ := json.Marshal(M_Info)
							logger.Info("-->>M_info: %s", string(resStr))
							resStr, _ = json.Marshal(A_Info)
							logger.Info("-->>A_info: %s", string(resStr))
							resStr, _ = json.Marshal(B_Info)
							logger.Info("-->>B_info: %s", string(resStr))
							logger.Info("-->>TradeProccess Overflow:%v", overFlow)
							tradeClient.TradeProccess(A_Info.Obj, B_Info.Obj, fnd_M, int64(tradeVol), overFlow)
						}
						logger.Info("-->>-----------------------------------------------------")
					}
				}
			}
		}
	}()
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c
	quoteClient.stop()
	tradeClient.stop()
	waitExit.Wait()
	time.Sleep(time.Millisecond * 200)
}
